function PhotoPuzzle(view, initialPositions) {
	this.view = view;
	this.positions = initialPositions;
	this.selected = -1;
}

PhotoPuzzle.prototype.startup = function() {
	this.view.init(this);
	this.updateView();
};

PhotoPuzzle.prototype.updateView = function() {
	this.view.update({
		positions : this.positions,
		selected : this.selected
	});
};

PhotoPuzzle.prototype.onSelect = function(index) {
	this.selected = index;
	this.updateView();
};

PhotoPuzzle.prototype.onSwap = function(swapIndex) {
	var swapped = false;

	if (this.selected >= 0) {
		swapped = true;
		this.swap(this.selected, swapIndex);
		this.selected = -1;
		this.updateView();
	}

	if (this.checkSolved()) {
		new AEnigmatesStorage("photopuzzle").solved();
		this.view.onSolved();
	}

	return swapped;
};

PhotoPuzzle.prototype.swap = function(a, b) {
	var tmp = this.positions[a];
	this.positions[a] = this.positions[b];
	this.positions[b] = tmp;
};

PhotoPuzzle.prototype.checkSolved = function() {
	var solved = true;

	var shift = this.positions[0];
	for ( var i = 1; i < this.positions.length; i++) {
		if (this.positions[i] != i + shift) {
			solved = false;
			break;
		}
	}

	return solved;
};

/**
 * 
 * @returns
 */
function AsyncExecutor(job) {
	this.job = job;
	this.stepCount = 0;
}

AsyncExecutor.prototype.start = function() {
	var self = this;
	function next() {
		self.start();
	}

	this.job(next, this.stepCount++);
};

/**
 * 
 * @returns
 */
function AsyncShuffler(data) {
	this.data = data;
	this.shufflePath = this.createShufflePath(data.length);
	this.counter = data.length;
}

AsyncShuffler.prototype.createShufflePath = function(n) {
	var result = [];

	for ( var i = n - 1; i >= 0; i--) {
		result[i] = i;
	}

	return shuffle(result);
};

AsyncShuffler.prototype.start = function(step, done) {
	this.done = done;
	this.step = step;
	this.executor = new AsyncExecutor(this.createStep());
	this.executor.start();
};

AsyncShuffler.prototype.createStep = function() {
	var self = this;
	return function(next, stepCount) {
		self.onStep(next, stepCount);
	};
};

AsyncShuffler.prototype.onStep = function(next, stepCount) {
	if (this.counter > 0) {
		var lowerIndex = Math.floor(Math.random() * this.counter);
		lowerIndex = this.shufflePath[lowerIndex];

		this.counter--;
		var upperIndex = this.shufflePath[this.counter];

		var temp = this.data[upperIndex];
		this.data[upperIndex] = this.data[lowerIndex];
		this.data[lowerIndex] = temp;

		this.step(this.data, next, stepCount);
	} else {
		this.done();
	}
};

function shuffle(array) {
	var counter = array.length, temp, index;

	while (counter > 0) {
		index = Math.floor(Math.random() * counter);

		counter--;

		temp = array[counter];
		array[counter] = array[index];
		array[index] = temp;
	}

	return array;
}